namespace Microsoft.ServiceModel.Samples.Discovery.Protocol
{
    using System;
    using System.Xml;
    using System.Diagnostics;
    using System.Collections.Generic;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    
    public abstract class FindRequest
    {
        OperationContext opContext;
        IFindResponseContract responseChannel;
        EndpointAddress replyTo;
        MessageHeaders outHeaders;
        object thisLock;

        internal protected FindRequest(OperationContext opContext)
        {
            this.opContext = opContext;
            this.thisLock = new object();
        }

        protected OperationContext OperationContext
        {
            get
            {
                return this.opContext;
            }
        }

        protected IFindResponseContract ResponseChannel
        {
            get
            {
                return this.responseChannel;
            }
        }

        protected EndpointAddress ReplyTo
        {
            get
            {
                return this.replyTo;
            }
        }

        protected MessageHeaders OutgoingMessageHeaders
        {
            get
            {
                return this.outHeaders;
            }
        }

        protected object ThisLock
        {
            get
            {
                return this.thisLock;
            }
        }

        public void SendHello(ServiceProperties serviceProperties)
        {
            EnsureResponseChannel();

            using (new OperationContextScope((IContextChannel)ResponseChannel))
            {
                AddOutgoingMessageHeaders();

                HelloMessage message = new HelloMessage();
                message.AppSequence = new AppSequence();
                message.AppSequence.InstanceId = ProtocolUtility.CreateAppSequenceInstanceId();
                message.AppSequence.SequenceId = Guid.NewGuid().ToString();
                message.AppSequence.MessageNumber = 0;
                message.Hello = Hello.FromServiceProperties(serviceProperties);

                ResponseChannel.HelloOperation(message);
            }
        }

        protected void EnsureResponseChannel()
        {
            if (this.responseChannel == null)
            {
                lock (thisLock)
                {
                    if (this.responseChannel == null)
                    {
                        this.responseChannel = this.opContext.GetCallbackChannel<IFindResponseContract>();
                        this.replyTo = this.opContext.IncomingMessageHeaders.ReplyTo;
                        this.outHeaders = new MessageHeaders(this.opContext.IncomingMessageVersion);

                        foreach (AddressHeader addrHeader in replyTo.Headers)
                            outHeaders.Add(addrHeader.ToMessageHeader());
                    }
                }
            }

            Debug.Assert(this.responseChannel != null);
            Debug.Assert(this.replyTo != null);
        }

        protected void AddOutgoingMessageHeaders()
        {
            OperationContext.Current.OutgoingMessageHeaders.To = this.replyTo.Uri;
            OperationContext.Current.OutgoingMessageHeaders.CopyHeadersFrom(this.outHeaders);
            OperationContext.Current.OutgoingMessageHeaders.MessageId = new UniqueId();
            OperationContext.Current.OutgoingMessageHeaders.RelatesTo = 
                this.opContext.IncomingMessageHeaders.MessageId;
        }
    }
}
